using System;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
using System.Diagnostics;

using FlickrNet;
using System.Security.Permissions;

namespace Yorrick.FlickrMetadataSynchr.Flickr
{
    /// <summary>
    /// Exception class for exceptions thrown by the <see cref="FlickrHelper"/> class.
    /// </summary>
    [Serializable]
    public class FlickrHelperException : Exception
    {
        #region Private fields
        /// <summary>
        /// The Flickr error code.
        /// </summary>
        private int _code;
        #endregion

        #region Public properties
        /// <summary>
        /// Gets the Flickr error code.
        /// </summary>
        public int Code
        {
            get
            {
                return _code;
            }
        }
        #endregion

        #region Constructors
        /// <summary>
        /// Constructs an instance.
        /// </summary>
        public FlickrHelperException()
        {
        }

        /// <summary>
        /// Constructs an instance with an error message.
        /// </summary>
        /// <param name="message">The error message.</param>
        public FlickrHelperException(string message)
            : base(message)
        {
        }

        /// <summary>
        /// Constructs an instance with an error message and an inner exception.
        /// </summary>
        /// <param name="message">The error message.</param>
        /// <param name="inner">The inner exception</param>
        public FlickrHelperException(string message, Exception inner)
            : base(message, inner)
        {
        }

        /// <summary>
        /// Constructs an instance with an error message, an error code and an inner exception.
        /// </summary>
        /// <param name="message">The error message.</param>
        /// <param name="code">The error code.</param>
        /// <param name="inner">The inner exception</param>
        public FlickrHelperException(string message, int code, Exception inner)
            : base(message, inner)
        {
            _code = code;
        }

        /// <summary>
        /// Constructor for serialization purposes.
        /// </summary>
        /// <param name="info">The serialization info.</param>
        /// <param name="context">The serialization context.</param>
        [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
        protected FlickrHelperException(
         System.Runtime.Serialization.SerializationInfo info,
         System.Runtime.Serialization.StreamingContext context)
            : base(info, context)
        {
            if (info == null)
            {
                throw new ArgumentNullException("info");
            }

            _code = info.GetInt32("Code");
        }
        #endregion

        #region Public static methods
        /// <summary>
        /// Returns a <see cref="FlickrHelperException"/> instance for the situation
        /// that the setting for a configuration setting is invalid.
        /// </summary>
        /// <param name="ex">The inner exception. Can be <c>null</c>.</param>
        /// <returns>A <see cref="FlickrHelperException"/> instance.</returns>
        public static FlickrHelperException GetInvalidConfigurationSettingException(string settingName, Exception ex)
        {
            string message = String.Format(CultureInfo.CurrentUICulture,
               Properties.Messages.InvalidConfigurationSetting, settingName);

            return new FlickrHelperException(message, ex);
        }

        /// <summary>
        /// Returns a <see cref="FlickrHelperException"/> instance for the situation
        /// that the application cannot connect to the Flickr webserver.
        /// </summary>
        /// <param name="ex">The inner exception.</param>
        /// <returns>A <see cref="FlickrHelperException"/> instance.</returns>
        public static FlickrHelperException GetFailedToConnectToFlickrException(Exception ex)
        {
            return new FlickrHelperException(Properties.Messages.ConnectFailed, FlickrErrorCodes.ConnectionFailure, ex);
        }

        /// <summary>
        /// Handles a <see cref="FlickrException"/> exception by wrapping it with a
        /// <see cref="FlickrHelperException"/>.
        /// </summary>
        /// <param name="ex">The exception object to wrap.</param>
        public static FlickrHelperException WrapFlickrException(FlickrException ex)
        {
            return WrapFlickrException(ex, Properties.Messages.GeneralFlickrError);
        }

        /// <summary>
        /// Handles an exception that occurs during saving a download by wrapping it with a
        /// <see cref="FlickrHelperException"/>.
        /// </summary>
        /// <param name="ex">The exception object.</param>
        public static FlickrHelperException GetDownloadSaveException(Exception ex)
        {
            string friendlyErrorMessage = Properties.Messages.SavingDownloadedPictureFailed;

            Trace.TraceError(friendlyErrorMessage + " Exception details follow:"
               + System.Environment.NewLine + ex.ToString());

            friendlyErrorMessage += " Reason: " + ex.Message;

            return new FlickrHelperException(friendlyErrorMessage, ex);
        }

        /// <summary>
        /// Handles a <see cref="FlickrException"/> exception.
        /// </summary>
        /// <param name="ex">The exception object.</param>
        /// <param name="friendlyErrorMessage">A friendly error message.</param>
        /// <exception cref="FlickrHelperException">Wraps the <see cref="FlickrException"/>.</exception>
        public static FlickrHelperException WrapFlickrException(FlickrException ex, string friendlyErrorMessage)
        {
            Trace.TraceError(friendlyErrorMessage + " Exception details follow:"
               + System.Environment.NewLine + ex.ToString());

            int apiErrorCode = FlickrErrorCodes.NoApiError;

            FlickrApiException apiEx = ex as FlickrApiException;
            if (apiEx != null)
            {
                apiErrorCode = apiEx.Code;
                if (apiErrorCode == FlickrErrorCodes.InvalidAuthorizationToken)
                {
                    friendlyErrorMessage += " " + Properties.Messages.ApplicationNotAuthorized;
                }
            }
            else
            {
                friendlyErrorMessage += System.Environment.NewLine + "(" + ex.Message + ")";
            }

            return new FlickrHelperException(friendlyErrorMessage, apiErrorCode, ex);
        }
        #endregion

        /// <summary>
        /// Used for serialization purposes.
        /// </summary>
        /// <param name="info">The <see cref="SerializationInfo"/> instance to use.</param>
        /// <param name="context">Not used.</param>
        [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
        public override void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
        {
            if (info == null)
            {
                throw new ArgumentNullException("info");
            }

            base.GetObjectData(info, context);

            info.AddValue("Code", _code);
        }
    }
}
